﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TrackingSim.GraphicSystem.Models
{


    class ObjectModelLoader
    {
        private static int s_current_group_id = 0;
        private static ResourceSystem.load_delegate<ResourceSystem.Resource> loader = ObjectModelLoader.load;

        public ObjectModelLoader()
        {
        }

        public static ResourceSystem.load_delegate<ResourceSystem.Resource> Loader
        {
            get { return loader; }
        }

        private static void load(string path, List<ResourceSystem.Resource> object_models)
        {
            string[] file_lines = File.ReadAllLines(path);

            ++s_current_group_id;
            ObjectMesh obj = null;

            int last_slash = path.LastIndexOfAny(new char[] { '/', '\\' });
            if (last_slash == -1)
                last_slash = 0;

            int last_dot = path.LastIndexOf('.');
            string modelName = path.Substring(last_slash, last_dot);

            ObjectModel obj_model = new ObjectModel(modelName, path);
            obj_model.GroupName = modelName;
            obj_model.Name = modelName;

            List<Vector3> vertices = new List<Vector3>();
            List<Vector3> normals = new List<Vector3>();
            List<Face> faces = new List<Face>();
            List<ObjectMesh> meshes = new List<ObjectMesh>();

            //Gather all the data from the file
            foreach (string line in file_lines)
            {
                string[] line_parts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                if (line_parts.Length > 0)
                {
                    string line_type = line_parts[0];

                    switch (line_type)
                    {
                        case "v":
                            {
                                vertices.Add(new Vector3(Convert.ToSingle(line_parts[1]),
                                                         Convert.ToSingle(line_parts[2]),
                                                         Convert.ToSingle(line_parts[3])));
                                break;
                            }

                        case "vn":
                            {
                                normals.Add(new Vector3(Convert.ToSingle(line_parts[1]),
                                                        Convert.ToSingle(line_parts[2]),
                                                        Convert.ToSingle(line_parts[3])));
                                break;
                            }

                        case "f":
                            {
                                faces.Add(parseFace(line_parts));
                                break;
                            }

                        default:
                            {
                                break;
                            }
                    }
                }
            }

            //Create the individual meshes

            int current_obj_index = -1;
            int current_face_index = 0;

            foreach (string line in file_lines)
            {
                string[] line_parts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                if (line_parts.Length > 0)
                {
                    string line_type = line_parts[0];

                    switch (line_type)
                    {
                        case "o":
                            {
                                obj = new ObjectMesh();
                                obj.Name = line_parts[1];
                                obj.GroupName = modelName;
                                meshes.Add(obj);

                                ++current_obj_index;
                                break;
                            }
                        case "f":
                            {
                                meshes[current_obj_index].addFace(faces[current_face_index]);
                                ++current_face_index;
                                break;
                            }

                        default:
                            {
                                break;
                            }
                    }
                }
            }


            //Set up the objects' vertex buffers and add to the model mesh list
            foreach (ObjectMesh mesh in meshes)
            {
                mesh.buildVertices(vertices, normals);
                obj_model.Meshes.Add(mesh);
            }

            //Populate the Model to World matrix for this model.
            string  mat_path = path.Substring(0, last_dot) + ".txt";

            StreamReader sr = File.OpenText(mat_path);

            while (!sr.EndOfStream)
            {
                string mat_line = sr.ReadLine();
                string[] line_parts = mat_line.Split();

                switch (line_parts[0])
                {
                    case "X":
                        obj_model.ModelToWorld = Matrix.CreateRotationX(MathHelper.ToRadians(Convert.ToSingle(line_parts[1]))) * obj_model.ModelToWorld;
                        break;

                    case "Y":
                        obj_model.ModelToWorld = Matrix.CreateRotationY(MathHelper.ToRadians(Convert.ToSingle(line_parts[1]))) * obj_model.ModelToWorld;
                        break;

                    case "Z":
                        obj_model.ModelToWorld = Matrix.CreateRotationZ(MathHelper.ToRadians(Convert.ToSingle(line_parts[1]))) * obj_model.ModelToWorld;
                        break;
                }
            }


            object_models.Add(obj_model);

            vertices.Clear();
            normals.Clear();
            faces.Clear();
        }

        private static Face parseFace(string[] line_parts)
        {
            Face f = new Face();

            for (int i = 1; i < line_parts.Length; ++i)
            {
                string[] group_parts = line_parts[i].Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                //Vertex only
                if (group_parts.Length == 1)
                {
                    f.VertexIndices.Add(Convert.ToInt32(group_parts[0]));
                }
                else if (group_parts.Length == 2) //Vertex and normals
                {
                    f.VertexIndices.Add(Convert.ToInt32(group_parts[0]));
                    f.NormalIndices.Add(Convert.ToInt32(group_parts[1]));
                }
                else //Vertex, texture, and normals
                {
                    f.VertexIndices.Add(Convert.ToInt32(group_parts[0]));
                    f.TextureIndices.Add(Convert.ToInt32(group_parts[1]));
                    f.NormalIndices.Add(Convert.ToInt32(group_parts[2]));
                }

            }

            return f;
        }
    }
}
